import pygame
import random
import os
import sys
from typing import List, Dict, Tuple, Optional, Union

# 初始化Pygame
pygame.init()

# 设置中文字体支持
pygame.font.init()
font_options = ["SimHei", "WenQuanYi Micro Hei", "Heiti TC", "Arial Unicode MS"]
system_fonts = pygame.font.get_fonts()
chinese_font = None

for font in font_options:
    if font.lower() in system_fonts:
        chinese_font = font
        break

if not chinese_font:
    chinese_font = pygame.font.get_default_font()

# 游戏窗口设置
SCREEN_WIDTH = 480
SCREEN_HEIGHT = 720
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("羊了个羊")

# 颜色定义
BG_COLOR = (255, 255, 255)
BUTTON_COLOR = (0, 128, 255)
BUTTON_HOVER_COLOR = (0, 150, 255)
BUTTON_TEXT_COLOR = (0, 0, 0)
UNDO_BUTTON_COLOR = (192, 192, 192)
UNDO_BUTTON_HOVER_COLOR = (220, 220, 220)
SHUFFLE_BUTTON_COLOR = (255, 128, 0)
SHUFFLE_BUTTON_HOVER_COLOR = (255, 150, 50)

# 卡片尺寸和间距
CARD_WIDTH = 60
CARD_HEIGHT = 70
CARD_SPACING = 10
CARD_SHADOW_OFFSET = 2
SPACING = 20

# 游戏区域布局
HEADER_HEIGHT = 100
GRID_ROWS = 4
GRID_COLS = 4
grid_width = GRID_COLS * (CARD_WIDTH + CARD_SPACING) - CARD_SPACING
GRID_LEFT = (SCREEN_WIDTH - grid_width) // 2
GRID_TOP = HEADER_HEIGHT + 20
GRID_BOTTOM = GRID_TOP + GRID_ROWS * (CARD_HEIGHT + CARD_SPACING) - CARD_SPACING

# 槽位区域设置
SLOT_ROWS = 1
SLOT_COLS = 7
slot_width = SLOT_COLS * (CARD_WIDTH + CARD_SPACING // 2) - CARD_SPACING // 2
SLOT_LEFT = (SCREEN_WIDTH - slot_width) // 2
SLOT_TOP = SCREEN_HEIGHT - 120
SLOT_HEIGHT = CARD_HEIGHT + 30

# 按钮设置
BUTTON_WIDTH = 55
BUTTON_HEIGHT = 55
BUTTON_LEFT = (SCREEN_WIDTH - BUTTON_WIDTH) // 3
BUTTON_TOP = 20
button_used = False
item_use_count = 1

# 撤销按钮设置
UNDO_BUTTON_WIDTH = 55
UNDO_BUTTON_HEIGHT = 55
UNDO_BUTTON_LEFT = (SCREEN_WIDTH - UNDO_BUTTON_WIDTH) // 16 * 13 - UNDO_BUTTON_WIDTH
UNDO_BUTTON_TOP = 20
undo_stack = []
max_undo_steps = 1

# 洗牌按钮设置
SHUFFLE_BUTTON_WIDTH = 55
SHUFFLE_BUTTON_HEIGHT = 55
SHUFFLE_BUTTON_LEFT = (SCREEN_WIDTH - SHUFFLE_BUTTON_WIDTH) // 1 - SHUFFLE_BUTTON_WIDTH // 0.7
SHUFFLE_BUTTON_TOP = 20
shuffle_stack = []
max_shuffle_steps = 1
shuffle_available = True

# 粒子效果设置
particles = []

# 洗牌动画效果
shuffle_animation = False
shuffle_progress = 0
shuffle_positions = []

# 性能优化设置
VSYNC = True  # 垂直同步
FPS = 60  # 目标帧率
frame_delta = 0.0  # 帧间隔时间

# 加载背景图像
try:
    image_folder = os.path.join(os.path.dirname(__file__), "image")
    background_path = os.path.join(image_folder, "back.png")
    background_img = pygame.image.load(background_path)
    background_img = pygame.transform.scale(background_img, (SCREEN_WIDTH, SCREEN_HEIGHT))
    print("主背景图像加载成功")
except Exception as e:
    print(f"主背景图像加载失败: {e}")
    background_img = None

# 加载槽位背景图像
try:
    slot_bg_path = os.path.join(image_folder, "back2.png")
    slot_bg_img = pygame.image.load(slot_bg_path)
    slot_bg_img = pygame.transform.scale(slot_bg_img, (slot_width + 20, SLOT_HEIGHT + 20))
    print("槽位背景图像加载成功")
except Exception as e:
    print(f"槽位背景图像加载失败: {e}")
    slot_bg_img = None

# 加载卡片图片
card_images = []
try:
    for i in range(5):
        if i == 0:
            tile_path = os.path.join(image_folder, "tile.png")
        else:
            tile_path = os.path.join(image_folder, f"tile{i}.png")

        if os.path.exists(tile_path):
            img = pygame.image.load(tile_path)
            img = pygame.transform.scale(img, (CARD_WIDTH, CARD_HEIGHT))
            card_images.append(img)
            print(f"卡片图片 {os.path.basename(tile_path)} 加载成功")
        else:
            print(f"卡片图片 {tile_path} 不存在")

    if not card_images:
        raise Exception("没有找到任何卡片图片")
except Exception as e:
    print(f"卡片图片加载失败: {e}")
    COLORS = [
        (255, 0, 0),  # 红
        (0, 255, 0),  # 绿
        (0, 0, 255),  # 蓝
        (255, 255, 0),  # 黄
        (128, 0, 128),  # 紫
    ]

# 加载按钮背景图片
try:
    button_bg_path = os.path.join(image_folder, "eoch.jpg")
    button_bg_img = pygame.image.load(button_bg_path)
    button_bg_img = pygame.transform.scale(button_bg_img, (BUTTON_WIDTH, BUTTON_HEIGHT))
    print("按钮背景图像加载成功")
except Exception as e:
    print(f"按钮背景图像加载失败: {e}")
    button_bg_img = None

# 游戏状态
slots = []
score = 0
game_over = False
game_won = False

# 动画队列
animation_queue = []
ANIMATION_SPEED = 0.1


# 预渲染文本
def pre_render_text():
    """预渲染静态文本以提高性能"""
    font = pygame.font.SysFont(chinese_font, 36)
    capacity_text = font.render(f"{len(slots)}/{SLOT_COLS}", True, (100, 100, 100))

    undo_font = pygame.font.SysFont(chinese_font, 12)
    undo_text = undo_font.render("撤销", True, BUTTON_TEXT_COLOR)
    undo_count_font = pygame.font.SysFont(chinese_font, 10)

    shuffle_font = pygame.font.SysFont(chinese_font, 12)
    shuffle_text = shuffle_font.render("洗牌", True, BUTTON_TEXT_COLOR)

    button_font = pygame.font.SysFont(chinese_font, 12)
    button_text = button_font.render(f"清空卡槽", True, BUTTON_TEXT_COLOR)
    count_font = pygame.font.SysFont(chinese_font, 10)

    return {
        'capacity': capacity_text,
        'undo': undo_text,
        'undo_count_font': undo_count_font,
        'shuffle': shuffle_text,
        'button': button_text,
        'count': count_font
    }


text_cache = pre_render_text()


def check_victory():
    """检查是否胜利"""
    for row in grid:
        for stack in row:
            if len(stack) > 0:
                return False
    return len(slots) == 0


def create_particles(x, y, card_type):
    """创建消除时的粒子效果"""
    global particles
    if card_images and card_type < len(card_images):
        img = card_images[card_type]
        colors = []
        for _ in range(5):
            try:
                pixel_color = img.get_at((random.randint(5, CARD_WIDTH - 5), random.randint(5, CARD_HEIGHT - 5)))
                colors.append(pixel_color)
            except IndexError:
                colors.append((random.randint(100, 255), random.randint(100, 255), random.randint(100, 255)))
    else:
        colors = [COLORS[card_type]]

    for _ in range(20):
        particle = {
            'x': x + CARD_WIDTH // 2,
            'y': y + CARD_HEIGHT // 2,
            'size': random.randint(2, 6),
            'color': random.choice(colors),
            'vx': random.uniform(-3, 3),
            'vy': random.uniform(-3, 3),
            'life': random.uniform(0.5, 1.5)
        }
        particles.append(particle)


def update_particles():
    """更新粒子状态"""
    global particles
    for i in range(len(particles) - 1, -1, -1):
        p = particles[i]
        p['x'] += p['vx']
        p['y'] += p['vy']
        p['life'] -= 0.02 * frame_delta * FPS  # 使用帧间隔时间标准化动画速度
        if p['life'] <= 0:
            particles.pop(i)


def draw_particles():
    """绘制所有粒子"""
    for p in particles:
        alpha = int(p['life'] * 255)
        if alpha > 0:
            s = pygame.Surface((p['size'] * 2, p['size'] * 2), pygame.SRCALPHA)
            color = p['color']

            if len(color) == 3:
                r = max(0, min(255, int(color[0])))
                g = max(0, min(255, int(color[1])))
                b = max(0, min(255, int(color[2])))
                color = (r, g, b, alpha)
            elif len(color) >= 4:
                r = max(0, min(255, int(color[0])))
                g = max(0, min(255, int(color[1])))
                b = max(0, min(255, int(color[2])))
                a = max(0, min(255, int(color[3])))
                color = (r, g, b, a)
            else:
                color = (255, 255, 255, alpha)

            pygame.draw.circle(s, color, (p['size'], p['size']), p['size'])
            screen.blit(s, (p['x'] - p['size'], p['y'] - p['size']))


def check_cross_elimination():
    """检查卡槽中是否有三张相同的卡片并消除"""
    global slots, score
    eliminated = False
    card_count = {}
    for card in slots:
        if card in card_count:
            card_count[card] += 1
        else:
            card_count[card] = 1

    for card, count in card_count.items():
        if count >= 3:
            new_slots = []
            remaining = 3
            eliminated_positions = []
            for i, s in enumerate(slots):
                if s == card and remaining > 0:
                    remaining -= 1
                    eliminated_positions.append(i)
                else:
                    new_slots.append(s)

            for pos in eliminated_positions:
                x = SLOT_LEFT + pos * (CARD_WIDTH + 5)
                y = SLOT_TOP
                create_particles(x, y, card)

            slots = new_slots
            score += 1
            eliminated = True

    return eliminated


# 初始化卡片堆
grid = []
for row in range(GRID_ROWS):
    grid_row = []
    for col in range(GRID_COLS):
        layers = random.randint(3, 5)
        max_card_type = len(card_images) - 1 if card_images else len(COLORS) - 1
        stack = [random.randint(0, max_card_type) for _ in range(layers)]
        stack += [stack[-1]] * (3 - layers % 3) if layers % 3 != 0 else []
        grid_row.append(stack)
    grid.append(grid_row)


def save_game_state(row, col, card):
    """保存当前游戏状态用于撤销"""
    global undo_stack
    state = {
        'grid': [[stack.copy() for stack in row] for row in grid],
        'slots': slots.copy(),
        'score': score,
        'row': row,
        'col': col,
        'card': card
    }
    undo_stack.append(state)
    if len(undo_stack) > max_undo_steps:
        undo_stack.pop(0)


def undo_last_move():
    """撤销上一步操作"""
    global grid, slots, score, game_over, game_won
    if not undo_stack:
        return False

    state = undo_stack.pop()
    grid = state['grid']
    slots = state['slots']
    score = state['score']
    game_over = False
    game_won = False

    row = state['row']
    col = state['col']
    card = state['card']
    grid[row][col].append(card)

    return True


def save_shuffle_state():
    """保存洗牌前的游戏状态"""
    global shuffle_stack
    state = {
        'grid': [[stack.copy() for stack in row] for row in grid],
    }
    shuffle_stack.append(state)
    if len(shuffle_stack) > max_shuffle_steps:
        shuffle_stack.pop(0)


def shuffle_cards():
    """洗牌功能 - 打乱当前卡片布局"""
    global grid, shuffle_animation, shuffle_progress, shuffle_positions

    save_shuffle_state()

    all_cards = []
    for row in grid:
        for stack in row:
            all_cards.extend(stack)
            stack.clear()

    random.shuffle(all_cards)

    index = 0
    for row in range(GRID_ROWS):
        for col in range(GRID_COLS):
            layers = random.randint(3, 5)
            if index < len(all_cards):
                for _ in range(layers):
                    if index < len(all_cards):
                        grid[row][col].append(all_cards[index])
                        index += 1
                    else:
                        break

    shuffle_animation = True
    shuffle_progress = 0

    shuffle_positions = []
    for row in range(GRID_ROWS):
        for col in range(GRID_COLS):
            stack = grid[row][col]
            for i, card_type in enumerate(stack):
                x = GRID_LEFT + col * (CARD_WIDTH + SPACING)
                y = GRID_TOP + row * (CARD_HEIGHT + SPACING) - (i * 5)
                target_x = x + random.randint(-50, 50)
                target_y = y + random.randint(-50, 50)
                shuffle_positions.append({
                    'card_type': card_type,
                    'start_x': x,
                    'start_y': y,
                    'target_x': target_x,
                    'target_y': target_y,
                    'current_x': x,
                    'current_y': y,
                    'stack_row': row,
                    'stack_col': col,
                    'stack_index': i
                })


def undo_shuffle():
    """撤销洗牌操作"""
    global grid, shuffle_animation, shuffle_progress

    if not shuffle_stack:
        return False

    state = shuffle_stack.pop()
    grid = state['grid']

    shuffle_animation = False
    shuffle_progress = 0

    return True


# 创建按钮对象用于优化碰撞检测
class Button:
    def __init__(self, x, y, width, height, color, hover_color, text, action=None):
        self.rect = pygame.Rect(x, y, width, height)
        self.color = color
        self.hover_color = hover_color
        self.text = text
        self.action = action
        self.is_hovered = False

    def draw(self, surface):
        color = self.hover_color if self.is_hovered else self.color
        pygame.draw.rect(surface, color, self.rect)

        font = pygame.font.SysFont(chinese_font, 12)
        text_surface = font.render(self.text, True, BUTTON_TEXT_COLOR)
        text_rect = text_surface.get_rect(center=self.rect.center)
        surface.blit(text_surface, text_rect)

    def check_hover(self, pos):
        self.is_hovered = self.rect.collidepoint(pos)
        return self.is_hovered

    def handle_click(self):
        if self.action:
            self.action()


# 创建按钮实例
undo_button = Button(
    UNDO_BUTTON_LEFT, UNDO_BUTTON_TOP, UNDO_BUTTON_WIDTH, UNDO_BUTTON_HEIGHT,
    UNDO_BUTTON_COLOR, UNDO_BUTTON_HOVER_COLOR, "撤销", undo_last_move
)

shuffle_button = Button(
    SHUFFLE_BUTTON_LEFT, SHUFFLE_BUTTON_TOP, SHUFFLE_BUTTON_WIDTH, SHUFFLE_BUTTON_HEIGHT,
    SHUFFLE_BUTTON_COLOR, SHUFFLE_BUTTON_HOVER_COLOR, "洗牌",
    lambda: shuffle_cards() if shuffle_available and not shuffle_animation else None
)

clear_button = Button(
    BUTTON_LEFT, BUTTON_TOP, BUTTON_WIDTH, BUTTON_HEIGHT,
    BUTTON_COLOR, BUTTON_HOVER_COLOR, "清空卡槽",
    lambda: slots.clear() if not button_used and item_use_count > 0 else None
)

# 主循环
clock = pygame.time.Clock()
running = True

# 预计算常用值
card_sprite_cache = {}  # 缓存卡片图像


def get_card_sprite(card_type):
    """获取卡片图像，使用缓存提高性能"""
    if card_type not in card_sprite_cache:
        if card_images and card_type < len(card_images):
            card_sprite_cache[card_type] = card_images[card_type]
        else:
            safe_card_type = card_type % len(COLORS) if COLORS else 0
            surf = pygame.Surface((CARD_WIDTH, CARD_HEIGHT))
            surf.fill(COLORS[safe_card_type])
            card_sprite_cache[card_type] = surf
    return card_sprite_cache[card_type]


while running:
    # 计算帧间隔时间
    frame_delta = clock.tick(FPS) / 1000.0

    # 事件处理
    mouse_pos = pygame.mouse.get_pos()
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.MOUSEMOTION:
            # 仅更新鼠标悬停状态
            undo_button.check_hover(mouse_pos)
            shuffle_button.check_hover(mouse_pos)
            clear_button.check_hover(mouse_pos)
        elif event.type == pygame.MOUSEBUTTONDOWN and not game_over:
            if undo_button.check_hover(mouse_pos) and undo_stack:
                undo_button.handle_click()
            elif shuffle_button.check_hover(mouse_pos) and shuffle_available and not shuffle_animation:
                shuffle_button.handle_click()
            elif clear_button.check_hover(mouse_pos) and not button_used and item_use_count > 0:
                clear_button.handle_click()
                button_used = True
                item_use_count = 0
            else:
                # 计算点击位置
                grid_col = (mouse_pos[0] - GRID_LEFT) // (CARD_WIDTH + SPACING)
                grid_row = (mouse_pos[1] - GRID_TOP) // (CARD_HEIGHT + SPACING)
                # 检查点击范围
                if 0 <= grid_row < GRID_ROWS and 0 <= grid_col < GRID_COLS:
                    if len(grid[grid_row][grid_col]) > 0:
                        if len(slots) >= SLOT_COLS:
                            game_over = True
                            continue

                        card = grid[grid_row][grid_col].pop()
                        save_game_state(grid_row, grid_col, card)

                        animation_queue.append({
                            "card": card,
                            "start_x": GRID_LEFT + grid_col * (CARD_WIDTH + SPACING),
                            "start_y": GRID_TOP + grid_row * (CARD_HEIGHT + SPACING) - (
                                        len(grid[grid_row][grid_col]) * 5),
                            "end_x": SLOT_LEFT + len(slots) * (CARD_WIDTH + 5),
                            "end_y": SLOT_TOP,
                            "progress": 0
                        })

    # 处理洗牌动画
    if shuffle_animation:
        shuffle_progress += 0.02 * frame_delta * FPS
        if shuffle_progress >= 1:
            shuffle_animation = False
            shuffle_progress = 0
        else:
            for pos in shuffle_positions:
                t = shuffle_progress
                ease_t = t * t * (3 - 2 * t)
                pos['current_x'] = pos['start_x'] + (pos['target_x'] - pos['start_x']) * ease_t
                pos['current_y'] = pos['start_y'] + (pos['target_y'] - pos['start_y']) * ease_t

    # 处理动画队列
    if animation_queue and not game_over and not shuffle_animation:
        current_animation = animation_queue[0]
        current_animation["progress"] += ANIMATION_SPEED * frame_delta * FPS
        if current_animation["progress"] >= 1:
            if len(slots) < SLOT_COLS:
                slots.append(current_animation["card"])
            animation_queue.pop(0)

            while check_cross_elimination():
                pass

            if len(slots) >= SLOT_COLS:
                game_over = True
                animation_queue = []

            if check_victory():
                game_won = True
                game_over = True
                animation_queue = []

    # 更新粒子
    update_particles()

    # 绘制界面
    if background_img:
        screen.blit(background_img, (0, 0))
    else:
        screen.fill(BG_COLOR)

    # 绘制按钮
    undo_button.draw(screen)
    shuffle_button.draw(screen)

    # 显示撤销步数
    undo_count_text = text_cache['undo_count_font'].render(f"{len(undo_stack)}/{max_undo_steps}", True, (100, 100, 100))
    screen.blit(undo_count_text, (UNDO_BUTTON_LEFT, UNDO_BUTTON_TOP + UNDO_BUTTON_HEIGHT + 5))

    # 显示洗牌可用状态
    shuffle_count_text = text_cache['undo_count_font'].render("" if shuffle_available else "已用", True,
                                                              (100, 100, 100))
    screen.blit(shuffle_count_text, (SHUFFLE_BUTTON_LEFT, SHUFFLE_BUTTON_TOP + SHUFFLE_BUTTON_HEIGHT + 5))

    # 绘制清空卡槽按钮
    if button_bg_img:
        screen.blit(button_bg_img, (BUTTON_LEFT, BUTTON_TOP))
    else:
        clear_button.draw(screen)

    # 显示道具使用次数
    screen.blit(text_cache['button'],
                (BUTTON_LEFT + BUTTON_WIDTH // 2 - text_cache['button'].get_width() // 2, BUTTON_TOP + BUTTON_HEIGHT))
    count_text = text_cache['count'].render(f"次数: {item_use_count}", True, (100, 100, 100))
    screen.blit(count_text,
                (BUTTON_LEFT + BUTTON_WIDTH // 2 - count_text.get_width() // 2, BUTTON_TOP + BUTTON_HEIGHT + 15))

    # 绘制洗牌动画中的卡片
    if shuffle_animation:
        for pos in shuffle_positions:
            screen.blit(get_card_sprite(pos['card_type']), (pos['current_x'], pos['current_y']))
    else:
        # 绘制卡片堆 - 使用缓存的卡片图像
        for row in range(GRID_ROWS):
            for col in range(GRID_COLS):
                x = GRID_LEFT + col * (CARD_WIDTH + SPACING)
                y = GRID_TOP + row * (CARD_HEIGHT + SPACING)
                stack = grid[row][col]
                for i, card_type in enumerate(stack):
                    offset = i * 5
                    screen.blit(get_card_sprite(card_type), (x, y - offset))

    # 绘制槽位背景
    if slot_bg_img:
        screen.blit(slot_bg_img, (SLOT_LEFT - 10, SLOT_TOP - 10))
    else:
        pygame.draw.rect(screen, (200, 200, 200),
                         (SLOT_LEFT - 5, SLOT_TOP - 5,
                          slot_width + 10, SLOT_HEIGHT + 10),
                         border_radius=10)

    # 绘制槽位中的卡片
    for i, card in enumerate(slots):
        x = SLOT_LEFT + i * (CARD_WIDTH + 5)
        screen.blit(get_card_sprite(card), (x, SLOT_TOP))

    # 绘制动画中的卡片
    for anim in animation_queue:
        current_x = anim["start_x"] + (anim["end_x"] - anim["start_x"]) * anim["progress"]
        current_y = anim["start_y"] + (anim["end_y"] - anim["start_y"]) * anim["progress"]
        screen.blit(get_card_sprite(anim["card"]), (current_x, current_y))

    # 绘制粒子
    draw_particles()

    # 绘制状态信息
    font = pygame.font.SysFont(chinese_font, 30)
    text = font.render(f"分数: {score}", True, (0, 0, 0))
    screen.blit(text, (15, 35))

    # 更新并绘制槽位容量提示
    capacity_text = font.render(f"{len(slots)}/{SLOT_COLS}", True, (100, 100, 100))
    screen.blit(capacity_text, (SCREEN_WIDTH - 60, 40))

    if game_over:
        game_over_font = pygame.font.SysFont(chinese_font, 48)
        text = game_over_font.render("你赢了!" if game_won else "游戏结束!",
                                     True, (0, 255, 0) if game_won else (255, 0, 0))
        screen.blit(text, (SCREEN_WIDTH // 2 - text.get_width() // 2, SCREEN_HEIGHT // 2 - 24))

        restart_font = pygame.font.SysFont(chinese_font, 24)
        restart_text = restart_font.render("按任空格重新开始", True, (0, 0, 0))
        screen.blit(restart_text, (SCREEN_WIDTH // 2 - restart_text.get_width() // 2, SCREEN_HEIGHT // 2 + 40))

    pygame.display.flip()

    # 游戏结束后等待按键重新开始
    if game_over:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False  # 新增：处理关闭窗口事件
            elif event.type == pygame.KEYDOWN:
                slots = []
                score = 0
                game_over = False
                game_won = False
                button_used = False
                item_use_count = 1
                animation_queue = []
                particles = []
                undo_stack = []
                shuffle_stack = []
                shuffle_available = True

                grid = []
                for row in range(GRID_ROWS):
                    grid_row = []
                    for col in range(GRID_COLS):
                        layers = random.randint(3, 5)
                        max_card_type = len(card_images) - 1 if card_images else len(COLORS) - 1
                        stack = [random.randint(0, max_card_type) for _ in range(layers)]
                        stack += [stack[-1]] * (3 - layers % 3) if layers % 3 != 0 else []
                        grid_row.append(stack)
                    grid.append(grid_row)

                # 重置性能优化相关状态
                card_sprite_cache.clear()

pygame.quit()